﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Snowdreamist.Collection;

namespace Snowdreamist.Windows.Document.Adapter
{
    public sealed class DocumentAdapter : IDocumentAdapter
    {
        public static DocumentAdapter Current
        {
            get
            {
                if (_current == null)
                    lock (_syncRoot)
                        if (_syncRoot == null)
                            _current = new DocumentAdapter();
                return _current;
            }
        }

        public Type TargetType
        {
            get
            {
                throw new NotSupportedException();
            }
        }

        public RichTextDocument Convert(object source)
        {
            return this.Convert(source, null);
        }

        public RichTextDocument Convert(object source, IDictionary<string, IElementAdapter> elementAdapters)
        {
             if (source == null)
                throw new ArgumentNullException("source");

            var type = source.GetType();

            AdapterMetadata adapterMetadata = null;
            if (this._adapters.TryGetValue(type, out adapterMetadata))
            {
                if (adapterMetadata.Adapter != null)
                {
                    if (elementAdapters == null)
                        return adapterMetadata.Adapter.Convert(source, adapterMetadata.ElementAdapters);
                    else
                        return adapterMetadata.Adapter.Convert(source, adapterMetadata.ElementAdapters.Concat(elementAdapters).Select(p => p.Value).ToDictionary(p => p.TargetElementIdentity));
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public void RegisterAdapter(Type sourceType, IDocumentAdapter adapter)
        {
            if (sourceType == null)
                throw new ArgumentNullException("sourceType");
            if (adapter == null)
                throw new ArgumentNullException("adapter");
            if (adapter is DocumentAdapter)
                throw new ArgumentException("Cannot add DocumentAdapter");

            if (!this._adapters.ContainsKey(sourceType))
            {
                AdapterMetadata adapterMetadata = new AdapterMetadata(sourceType)
                {
                    Adapter = adapter
                };
                this._adapters[sourceType] = adapterMetadata;
            }
            else
            {
                this._adapters[sourceType].Adapter = adapter;
            }
        }

        public bool RegisterAdapter(Type sourceType, string sourceElementIdentity, IElementAdapter elementAdapter)
        {
            if (sourceType == null)
                throw new ArgumentNullException("sourceType");
            if (sourceElementIdentity == null)
                throw new ArgumentNullException("elementType");
            if (elementAdapter == null)
                throw new ArgumentNullException("elementAdapter");

            AdapterMetadata adapterMetadata = null;
            if (!this._adapters.TryGetValue(sourceType, out adapterMetadata))
            {
                adapterMetadata = new AdapterMetadata(sourceType);
                this._adapters.Add(sourceType, adapterMetadata);
            }

            return adapterMetadata.ElementAdapters.TryAdd(sourceElementIdentity, elementAdapter);
        }

        public void UnregisterAdapter(Type sourceType)
        {
            if (sourceType == null)
                throw new ArgumentNullException("sourceType");

            if (this._adapters.ContainsKey(sourceType))
            {
                this._adapters.Remove(sourceType);
            }
        }

        public bool IsAdapterExists(Type sourceType)
        {
            if (sourceType == null)
                throw new ArgumentNullException("sourceType");

            return this._adapters.ContainsKey(sourceType);
        }

        #region private

        private volatile static DocumentAdapter _current = null;

        private readonly static object _syncRoot = new object();

        private readonly ThreadSafeDictionary<Type, AdapterMetadata> _adapters = new ThreadSafeDictionary<Type, AdapterMetadata>();

        private DocumentAdapter() { }

        #endregion
    }

    internal class AdapterMetadata
    {
        public AdapterMetadata(Type targetType)
        {
            if (targetType == null)
                throw new ArgumentNullException("targetType");
            this.TargetType = targetType;
            this.ElementAdapters = new ThreadSafeDictionary<string, IElementAdapter>();
        }

        public Type TargetType { get; private set; }

        public IDocumentAdapter Adapter { get; set; }

        public ThreadSafeDictionary<string, IElementAdapter> ElementAdapters { get; private set; }
    }
}
